home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / Shells / tcsh / Source / sh.file.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-21  |  20.1 KB  |  841 lines

  1. /* $Header: /u/christos/src/tcsh-6.03/RCS/sh.file.c,v 3.6 1992/10/05 02:41:30 christos Exp $ */
  2. /*
  3.  * sh.file.c: File completion for csh. This file is not used in tcsh.
  4.  */
  5. /*-
  6.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Redistribution and use in source and binary forms, with or without
  10.  * modification, are permitted provided that the following conditions
  11.  * are met:
  12.  * 1. Redistributions of source code must retain the above copyright
  13.  *    notice, this list of conditions and the following disclaimer.
  14.  * 2. Redistributions in binary form must reproduce the above copyright
  15.  *    notice, this list of conditions and the following disclaimer in the
  16.  *    documentation and/or other materials provided with the distribution.
  17.  * 3. All advertising materials mentioning features or use of this software
  18.  *    must display the following acknowledgement:
  19.  *    This product includes software developed by the University of
  20.  *    California, Berkeley and its contributors.
  21.  * 4. Neither the name of the University nor the names of its contributors
  22.  *    may be used to endorse or promote products derived from this software
  23.  *    without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  26.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35.  * SUCH DAMAGE.
  36.  */
  37. #include "sh.h"
  38.  
  39. RCSID("$Id: sh.file.c,v 3.6 1992/10/05 02:41:30 christos Exp $")
  40.  
  41. #ifdef FILEC
  42.  
  43. /*
  44.  * Tenex style file name recognition, .. and more.
  45.  * History:
  46.  *    Author: Ken Greer, Sept. 1975, CMU.
  47.  *    Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
  48.  */
  49.  
  50. #define ON    1
  51. #define OFF    0
  52. #ifndef TRUE
  53. #define TRUE 1
  54. #endif
  55. #ifndef FALSE
  56. #define FALSE 0
  57. #endif
  58.  
  59. #define ESC    '\033'
  60.  
  61. typedef enum {
  62.     LIST, RECOGNIZE
  63. }       COMMAND;
  64.  
  65. static    void     setup_tty        __P((int));
  66. static    void     back_to_col_1        __P((void));
  67. static    void     pushback        __P((Char *));
  68. static    void     catn            __P((Char *, Char *, int));
  69. static    void     copyn            __P((Char *, Char *, int));
  70. static    Char     filetype        __P((Char *, Char *));
  71. static    void     print_by_column    __P((Char *, Char *[], int));
  72. static    Char     *tilde            __P((Char *, Char *));
  73. static    void     retype            __P((void));
  74. static    void     beep            __P((void));
  75. static    void      print_recognized_stuff    __P((Char *));
  76. static    void     extract_dir_and_name    __P((Char *, Char *, Char *));
  77. static    Char    *getentry        __P((DIR *, int));
  78. static    void     free_items        __P((Char **));
  79. static    int     tsearch        __P((Char *, COMMAND, int));
  80. static    int     recognize        __P((Char *, Char *, int, int));
  81. static    int     is_prefix        __P((Char *, Char *));
  82. static    int     is_suffix        __P((Char *, Char *));
  83. static    int     ignored        __P((Char *));
  84.  
  85.  
  86. /*
  87.  * Put this here so the binary can be patched with adb to enable file
  88.  * completion by default.  Filec controls completion, nobeep controls
  89.  * ringing the terminal bell on incomplete expansions.
  90.  */
  91. bool    filec = 0;
  92.  
  93. static void
  94. setup_tty(on)
  95.     int     on;
  96. {
  97. #ifdef TERMIO
  98. # ifdef POSIX
  99.     static struct termios tchars;
  100. # else
  101.     static struct termio tchars;
  102. # endif /* POSIX */
  103.  
  104.     if (on) {
  105. # ifdef POSIX
  106.     (void) tcgetattr(SHIN, &tchars);
  107. # else
  108.         (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars);
  109. # endif /* POSIX */
  110.     tchars.c_cc[VEOL] = ESC;
  111.     if (tchars.c_lflag & ICANON)
  112. # ifdef POSIX
  113.         on = TCSANOW;
  114. # else
  115.         on = TCSETAW;
  116. # endif /* POSIX */
  117.     else {
  118. # ifdef POSIX
  119.         on = TCSAFLUSH;
  120. # else
  121.         on = TCSETAF;
  122. # endif /* POSIX */
  123.         tchars.c_lflag |= ICANON;
  124.     
  125.     }
  126. #ifdef POSIX
  127.         (void) tcsetattr(SHIN, on, &tchars);
  128. #else
  129.         (void) ioctl(SHIN, on, (ioctl_t) &tchars);
  130. #endif /* POSIX */
  131.     }
  132.     else {
  133.     tchars.c_cc[VEOL] = _POSIX_VDISABLE;
  134. # ifdef POSIX
  135.     (void) tcsetattr(SHIN, TCSANOW, &tchars);
  136. # else
  137.         (void) ioctl(SHIN, TCSETAW, (ioctl_t) &tchars);
  138. # endif /* POSIX */
  139.     }
  140. #else
  141.     struct sgttyb sgtty;
  142.     static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */
  143.  
  144.     if (on) {
  145.     (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars);
  146.     tchars.t_brkc = ESC;
  147.     (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
  148.     /*
  149.      * This must be done after every command: if the tty gets into raw or
  150.      * cbreak mode the user can't even type 'reset'.
  151.      */
  152.     (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty);
  153.     if (sgtty.sg_flags & (RAW | CBREAK)) {
  154.         sgtty.sg_flags &= ~(RAW | CBREAK);
  155.         (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty);
  156.     }
  157.     }
  158.     else {
  159.     tchars.t_brkc = -1;
  160.     (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
  161.     }
  162. #endif /* TERMIO */
  163. }
  164.  
  165. /*
  166.  * Move back to beginning of current line
  167.  */
  168. static void
  169. back_to_col_1()
  170. {
  171. #ifdef TERMIO
  172. # ifdef POSIX
  173.     struct termios tty, tty_normal;
  174. # else
  175.     struct termio tty, tty_normal;
  176. # endif /* POSIX */
  177. #else
  178.     struct sgttyb tty, tty_normal;
  179. #endif /* TERMIO */
  180.  
  181. # ifdef BSDSIGS
  182.     sigmask_t omask = sigblock(sigmask(SIGINT));
  183. # else
  184.     sighold(SIGINT);
  185. # endif /* BSDSIGS */
  186.  
  187. #ifdef TERMIO
  188. # ifdef POSIX
  189.     (void) tcgetattr(SHOUT, &tty);
  190. # else
  191.     (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal);
  192. # endif /* POSIX */
  193.     tty_normal = tty;
  194.     tty.c_iflag &= ~INLCR;
  195.     tty.c_oflag &= ~ONLCR;
  196. # ifdef POSIX
  197.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  198. # else
  199.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  200. # endif /* POSIX */
  201.     (void) write(SHOUT, "\r", 1);
  202. # ifdef POSIX
  203.     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
  204. # else
  205.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
  206. # endif /* POSIX */
  207. #else
  208.     (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty);
  209.     tty_normal = tty;
  210.     tty.sg_flags &= ~CRMOD;
  211.     (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty);
  212.     (void) write(SHOUT, "\r", 1);
  213.     (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal);
  214. #endif /* TERMIO */
  215.  
  216. # ifdef BSDSIGS
  217.     (void) sigsetmask(omask);
  218. # else
  219.     (void) sigrelse(SIGINT);
  220. # endif /* BSDISGS */
  221. }
  222.  
  223. /*
  224.  * Push string contents back into tty queue
  225.  */
  226. static void
  227. pushback(string)
  228.     Char   *string;
  229. {
  230.     register Char *p;
  231.     char    c;
  232. #ifdef TERMIO
  233. # ifdef POSIX
  234.     struct termios tty, tty_normal;
  235. # else
  236.     struct termio tty, tty_normal;
  237. # endif /* POSIX */
  238. #else
  239.     struct sgttyb tty, tty_normal;
  240. #endif /* TERMIO */
  241.  
  242. #ifdef BSDSIGS
  243.     sigmask_t omask = sigblock(sigmask(SIGINT));
  244. #else
  245.     sighold(SIGINT);
  246. #endif /* BSDSIGS */
  247.  
  248. #ifdef TERMIO
  249. # ifdef POSIX
  250.     (void) tcgetattr(SHOUT, &tty);
  251. # else
  252.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  253. # endif /* POSIX */
  254.     tty_normal = tty;
  255.     tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
  256. # ifdef POSIX
  257.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  258. # else
  259.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  260. # endif /* POSIX */
  261.  
  262.     for (p = string; c = *p; p++)
  263.     (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
  264. # ifdef POSIX
  265.     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
  266. # else
  267.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
  268. # endif /* POSIX */
  269.     (void) sigsetmask(omask);
  270. #else
  271.     (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty);
  272.     tty_normal = tty;
  273.     tty.sg_flags &= ~ECHO;
  274.     (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty);
  275.  
  276.     for (p = string; c = *p; p++)
  277.     (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
  278.     (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal);
  279. #endif /* TERMIO */
  280.  
  281. # ifdef BSDSIGS
  282.     (void) sigsetmask(omask);
  283. # else
  284.     (void) sigrelse(SIGINT);
  285. # endif /* BSDISGS */
  286. }
  287.  
  288. /*
  289.  * Concatenate src onto tail of des.
  290.  * Des is a string whose maximum length is count.
  291.  * Always null terminate.
  292.  */
  293. static void
  294. catn(des, src, count)
  295.     register Char *des, *src;
  296.     register count;
  297. {
  298.     while (--count >= 0 && *des)
  299.     des++;
  300.     while (--count >= 0)
  301.     if ((*des++ = *src++) == 0)
  302.         return;
  303.     *des = '\0';
  304. }
  305.  
  306. /*
  307.  * Like strncpy but always leave room for trailing \0
  308.  * and always null terminate.
  309.  */
  310. static void
  311. copyn(des, src, count)
  312.     register Char *des, *src;
  313.     register count;
  314. {
  315.     while (--count >= 0)
  316.     if ((*des++ = *src++) == 0)
  317.         return;
  318.     *des = '\0';
  319. }
  320.  
  321. static  Char
  322. filetype(dir, file)
  323.     Char   *dir, *file;
  324. {
  325.     Char    path[MAXPATHLEN];
  326.     struct stat statb;
  327.  
  328.     catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
  329.     if (lstat(short2str(path), &statb) == 0) {
  330.     switch (statb.st_mode & S_IFMT) {
  331.     case S_IFDIR:
  332.         return ('/');
  333.  
  334.     case S_IFLNK:
  335.         if (stat(short2str(path), &statb) == 0 &&    /* follow it out */
  336.         S_ISDIR(statb.st_mode))
  337.         return ('>');
  338.         else
  339.         return ('@');
  340.  
  341.     case S_IFSOCK:
  342.         return ('=');
  343.  
  344.     default:
  345.         if (statb.st_mode & 0111)
  346.         return ('*');
  347.     }
  348.     }
  349.     return (' ');
  350. }
  351.  
  352. static struct winsize win;
  353.  
  354. /*
  355.  * Print sorted down columns
  356.  */
  357. static void
  358. print_by_column(dir, items, count)
  359.     Char   *dir, *items[];
  360.     int     count;
  361. {
  362.     register int i, rows, r, c, maxwidth = 0, columns;
  363.  
  364.     if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
  365.     win.ws_col = 80;
  366.     for (i = 0; i < count; i++)
  367.     maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
  368.     maxwidth += 2;        /* for the file tag and space */
  369.     columns = win.ws_col / maxwidth;
  370.     if (columns == 0)
  371.     columns = 1;
  372.     rows = (count + (columns - 1)) / columns;
  373.     for (r = 0; r < rows; r++) {
  374.     for (c = 0; c < columns; c++) {
  375.         i = c * rows + r;
  376.         if (i < count) {
  377.         register int w;
  378.  
  379.         xprintf("%S", items[i]);
  380.         xputchar(dir ? filetype(dir, items[i]) : ' ');
  381.         if (c < columns - 1) {    /* last column? */
  382.             w = Strlen(items[i]) + 1;
  383.             for (; w < maxwidth; w++)
  384.             xputchar(' ');
  385.         }
  386.         }
  387.     }
  388.     xputchar('\r');
  389.     xputchar('\n');
  390.     }
  391. }
  392.  
  393. /*
  394.  * Expand file name with possible tilde usage
  395.  *    ~person/mumble
  396.  * expands to
  397.  *    home_directory_of_person/mumble
  398.  */
  399. static Char *
  400. tilde(new, old)
  401.     Char   *new, *old;
  402. {
  403.     register Char *o, *p;
  404.     register struct passwd *pw;
  405.     static Char person[40];
  406.  
  407.     if (old[0] != '~')
  408.     return (Strcpy(new, old));
  409.  
  410.     for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++);
  411.     *p = '\0';
  412.     if (person[0] == '\0')
  413.     (void) Strcpy(new, value(STRhome));
  414.     else {
  415.     pw = getpwnam(short2str(person));
  416.     if (pw == NULL)
  417.         return (NULL);
  418.     (void) Strcpy(new, str2short(pw->pw_dir));
  419.     }
  420.     (void) Strcat(new, o);
  421.     return (new);
  422. }
  423.  
  424. /*
  425.  * Cause pending line to be printed
  426.  */
  427. static void
  428. retype()
  429. {
  430. #ifdef TERMIO
  431. # ifdef POSIX
  432.     struct termios tty;
  433.  
  434.     (void) tcgetattr(SHOUT, &tty);
  435. # else
  436.     struct termio tty;
  437.  
  438.     (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty);
  439. # endif /* POSIX */
  440.  
  441.     tty.c_lflag |= PENDIN;
  442.  
  443. # ifdef POSIX
  444.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  445. # else
  446.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  447. # endif /* POSIX */
  448. #else
  449.     int     pending_input = LPENDIN;
  450.  
  451.     (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input);
  452. #endif /* TERMIO */
  453. }
  454.  
  455. static void
  456. beep()
  457. {
  458.     if (adrof(STRnobeep) == 0)
  459.     (void) write(SHOUT, "\007", 1);
  460. }
  461.  
  462. /*
  463.  * Erase that silly ^[ and
  464.  * print the recognized part of the string
  465.  */
  466. static void
  467. print_recognized_stuff(recognized_part)
  468.     Char   *recognized_part;
  469. {
  470.     /* An optimized erasing of that silly ^[ */
  471.     putraw('\b');
  472.     putraw('\b');
  473.     switch (Strlen(recognized_part)) {
  474.  
  475.     case 0:            /* erase two Characters: ^[ */
  476.     putraw(' ');
  477.     putraw(' ');
  478.     putraw('\b');
  479.     putraw('\b');
  480.     break;
  481.  
  482.     case 1:            /* overstrike the ^, erase the [ */
  483.     xprintf("%S", recognized_part);
  484.     putraw(' ');
  485.     putraw('\b');
  486.     break;
  487.  
  488.     default:            /* overstrike both Characters ^[ */
  489.     xprintf("%S", recognized_part);
  490.     break;
  491.     }
  492.     flush();
  493. }
  494.  
  495. /*
  496.  * Parse full path in file into 2 parts: directory and file names
  497.  * Should leave final slash (/) at end of dir.
  498.  */
  499. static void
  500. extract_dir_and_name(path, dir, name)
  501.     Char   *path, *dir, *name;
  502. {
  503.     register Char *p;
  504.  
  505.     p = Strrchr(path, '/');
  506.     if (p == NULL) {
  507.     copyn(name, path, MAXNAMLEN);
  508.     dir[0] = '\0';
  509.     }
  510.     else {
  511.     copyn(name, ++p, MAXNAMLEN);
  512.     copyn(dir, path, p - path);
  513.     }
  514. }
  515. /* atp vmsposix - I need to remove all the setpwent 
  516.  *          getpwent endpwent stuff. VMS_POSIX has getpwnam getpwuid
  517.  *          and getlogin. This needs fixing. (There is no access to 
  518.  *          pw->passwd in VMS - a secure system benefit :-| )
  519.  */
  520. static Char *
  521. getentry(dir_fd, looking_for_lognames)
  522.     DIR    *dir_fd;
  523.     int     looking_for_lognames;
  524. {
  525.     register struct passwd *pw;
  526.     register struct dirent *dirp;
  527.  
  528.     if (looking_for_lognames) {
  529. #ifdef _VMS_POSIX
  530.         return (NULL);
  531. #else
  532.     if ((pw = getpwent()) == NULL)
  533.         return (NULL);
  534.     return (str2short(pw->pw_name));
  535. #endif /* atp vmsposix */
  536.     }
  537.     if (dirp = readdir(dir_fd))
  538.     return (str2short(dirp->d_name));
  539.     return (NULL);
  540. }
  541.  
  542. static void
  543. free_items(items)
  544.     register Char **items;
  545. {
  546.     register int i;
  547.  
  548.     for (i = 0; items[i]; i++)
  549.     xfree((ptr_t) items[i]);
  550.     xfree((ptr_t) items);
  551. }
  552.  
  553. #ifdef BSDSIGS
  554. # define FREE_ITEMS(items) { \
  555.     sigmask_t omask;\
  556. \
  557.     omask = sigblock(sigmask(SIGINT));\
  558.     free_items(items);\
  559.     items = NULL;\
  560.     (void) sigsetmask(omask);\
  561. }
  562. #else
  563. # define FREE_ITEMS(items) { \
  564.     (void) sighold(SIGINT);\
  565.     free_items(items);\
  566.     items = NULL;\
  567.     (void) sigrelse(SIGINT);\
  568. }
  569. #endif /* BSDSIGS */
  570.  
  571. /*
  572.  * Perform a RECOGNIZE or LIST command on string "word".
  573.  */
  574. static int
  575. tsearch(word, command, max_word_length)
  576.     Char   *word;
  577.     int     max_word_length;
  578.     COMMAND command;
  579. {
  580.     static Char **items = NULL;
  581.     register DIR *dir_fd;
  582.     register numitems = 0, ignoring = TRUE, nignored = 0;
  583.     register name_length, looking_for_lognames;
  584.     Char    tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
  585.     Char    name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
  586.     Char   *entry;
  587.  
  588. #define MAXITEMS 1024
  589.  
  590.     if (items != NULL)
  591.     FREE_ITEMS(items);
  592.  
  593.     looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
  594.     if (looking_for_lognames) {
  595. #ifndef _VMS_POSIX
  596.     (void) setpwent();
  597. #endif /*atp vmsposix */
  598.     copyn(name, &word[1], MAXNAMLEN);    /* name sans ~ */
  599.     dir_fd = NULL;
  600.     }
  601.     else {
  602.     extract_dir_and_name(word, dir, name);
  603.     if (tilde(tilded_dir, dir) == 0)
  604.         return (0);
  605.     dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
  606.     if (dir_fd == NULL)
  607.         return (0);
  608.     }
  609.  
  610. again:                /* search for matches */
  611.     name_length = Strlen(name);
  612.     for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames);) {
  613.     if (!is_prefix(name, entry))
  614.         continue;
  615.     /* Don't match . files on null prefix match */
  616.     if (name_length == 0 && entry[0] == '.' &&
  617.         !looking_for_lognames)
  618.         continue;
  619.     if (command == LIST) {
  620.         if (numitems >= MAXITEMS) {
  621.         xprintf("\nYikes!! Too many %s!!\n",
  622.             looking_for_lognames ?
  623.             "names in password file" : "files");
  624.         break;
  625.         }
  626.         /*
  627.          * From Beto Appleton (beto@aixwiz.austin.ibm.com)
  628.          *    typing "./control-d" will cause the csh to core-dump.
  629.          *    the problem can be reproduce as following:
  630.          *     1. set ignoreeof
  631.          *     2. set filec
  632.          *     3. create a directory with 1050 files
  633.          *     4. typing "./control-d" will cause the csh to core-dump
  634.          * Solution: Add + 1 to MAXITEMS
  635.          */
  636.         if (items == NULL)
  637.         items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS + 1);
  638.         items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) *
  639.                            sizeof(Char));
  640.         copyn(items[numitems], entry, MAXNAMLEN);
  641.         numitems++;
  642.     }
  643.     else {            /* RECOGNIZE command */
  644.         if (ignoring && ignored(entry))
  645.         nignored++;
  646.         else if (recognize(extended_name,
  647.                    entry, name_length, ++numitems))
  648.         break;
  649.     }
  650.     }
  651.     if (ignoring && numitems == 0 && nignored > 0) {
  652.     ignoring = FALSE;
  653.     nignored = 0;
  654.     if (looking_for_lognames)
  655. #ifndef _VMS_POSIX
  656.         (void) setpwent();
  657. #endif /* atp vmsposix */
  658.     else
  659.         rewinddir(dir_fd);
  660.     goto again;
  661.     }
  662.  
  663.     if (looking_for_lognames)
  664. #ifndef _VMS_POSIX
  665.     (void) endpwent();
  666. #endif /*atp vmsposix */
  667.     else
  668.     (void) closedir(dir_fd);
  669.     if (numitems == 0)
  670.     return (0);
  671.     if (command == RECOGNIZE) {
  672.     if (looking_for_lognames)
  673.         copyn(word, STRtilde, 1);
  674.     else
  675.         /* put back dir part */
  676.         copyn(word, dir, max_word_length);
  677.     /* add extended name */
  678.     catn(word, extended_name, max_word_length);
  679.     return (numitems);
  680.     }
  681.     else {            /* LIST */
  682.     qsort((ptr_t) items, numitems, sizeof(items[0]), sortscmp);
  683.     print_by_column(looking_for_lognames ? NULL : tilded_dir,
  684.             items, numitems);
  685.     if (items != NULL)
  686.         FREE_ITEMS(items);
  687.     }
  688.     return (0);
  689. }
  690.  
  691. /*
  692.  * Object: extend what user typed up to an ambiguity.
  693.  * Algorithm:
  694.  * On first match, copy full entry (assume it'll be the only match)
  695.  * On subsequent matches, shorten extended_name to the first
  696.  * Character mismatch between extended_name and entry.
  697.  * If we shorten it back to the prefix length, stop searching.
  698.  */
  699. static int
  700. recognize(extended_name, entry, name_length, numitems)
  701.     Char   *extended_name, *entry;
  702.     int     name_length, numitems;
  703. {
  704.     if (numitems == 1)        /* 1st match */
  705.     copyn(extended_name, entry, MAXNAMLEN);
  706.     else {            /* 2nd & subsequent matches */
  707.     register Char *x, *ent;
  708.     register int len = 0;
  709.  
  710.     x = extended_name;
  711.     for (ent = entry; *x && *x == *ent++; x++, len++);
  712.     *x = '\0';        /* Shorten at 1st Char diff */
  713.     if (len == name_length)    /* Ambiguous to prefix? */
  714.         return (-1);    /* So stop now and save time */
  715.     }
  716.     return (0);
  717. }
  718.  
  719. /*
  720.  * Return true if check matches initial Chars in template.
  721.  * This differs from PWB imatch in that if check is null
  722.  * it matches anything.
  723.  */
  724. static int
  725. is_prefix(check, template)
  726.     register Char *check, *template;
  727. {
  728.     do
  729.     if (*check == 0)
  730.         return (TRUE);
  731.     while (*check++ == *template++);
  732.     return (FALSE);
  733. }
  734.  
  735. /*
  736.  *  Return true if the Chars in template appear at the
  737.  *  end of check, I.e., are it's suffix.
  738.  */
  739. static int
  740. is_suffix(check, template)
  741.     Char   *check, *template;
  742. {
  743.     register Char *c, *t;
  744.  
  745.     for (c = check; *c++;);
  746.     for (t = template; *t++;);
  747.     for (;;) {
  748.     if (t == template)
  749.         return 1;
  750.     if (c == check || *--t != *--c)
  751.         return 0;
  752.     }
  753. }
  754.  
  755. int
  756. tenex(inputline, inputline_size)
  757.     Char   *inputline;
  758.     int     inputline_size;
  759. {
  760.     register int numitems, num_read;
  761.     char    tinputline[BUFSIZE];
  762.  
  763.  
  764.     setup_tty(ON);
  765.  
  766.     while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) {
  767.     int     i;
  768.     static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
  769.     '>', '(', ')', '|', '^', '%', '\0'};
  770.     register Char *str_end, *word_start, last_Char, should_retype;
  771.     register int space_left;
  772.     COMMAND command;
  773.  
  774.     for (i = 0; i < num_read; i++)
  775.         inputline[i] = (unsigned char) tinputline[i];
  776.     last_Char = inputline[num_read - 1] & ASCII;
  777.  
  778.     if (last_Char == '\n' || num_read == inputline_size)
  779.         break;
  780.     command = (last_Char == ESC) ? RECOGNIZE : LIST;
  781.     if (command == LIST)
  782.         xputchar('\n');
  783.     str_end = &inputline[num_read];
  784.     if (last_Char == ESC)
  785.         --str_end;        /* wipeout trailing cmd Char */
  786.     *str_end = '\0';
  787.     /*
  788.      * Find LAST occurence of a delimiter in the inputline. The word start
  789.      * is one Character past it.
  790.      */
  791.     for (word_start = str_end; word_start > inputline; --word_start)
  792.         if (Strchr(delims, word_start[-1]))
  793.         break;
  794.     space_left = inputline_size - (word_start - inputline) - 1;
  795.     numitems = tsearch(word_start, command, space_left);
  796.  
  797.     if (command == RECOGNIZE) {
  798.         /* print from str_end on */
  799.         print_recognized_stuff(str_end);
  800.         if (numitems != 1)    /* Beep = No match/ambiguous */
  801.         beep();
  802.     }
  803.  
  804.     /*
  805.      * Tabs in the input line cause trouble after a pushback. tty driver
  806.      * won't backspace over them because column positions are now
  807.      * incorrect. This is solved by retyping over current line.
  808.      */
  809.     should_retype = FALSE;
  810.     if (Strchr(inputline, '\t')) {    /* tab Char in input line? */
  811.         back_to_col_1();
  812.         should_retype = TRUE;
  813.     }
  814.     if (command == LIST)    /* Always retype after a LIST */
  815.         should_retype = TRUE;
  816.     if (should_retype)
  817.         printprompt();
  818.     pushback(inputline);
  819.     if (should_retype)
  820.         retype();
  821.     }
  822.     setup_tty(OFF);
  823.     return (num_read);
  824. }
  825.  
  826. static int
  827. ignored(entry)
  828.     register Char *entry;
  829. {
  830.     struct varent *vp;
  831.     register Char **cp;
  832.  
  833.     if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
  834.     return (FALSE);
  835.     for (; *cp != NULL; cp++)
  836.     if (is_suffix(entry, *cp))
  837.         return (TRUE);
  838.     return (FALSE);
  839. }
  840. #endif    /* FILEC */
  841.